// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: lucasyoo

#include "coin2/strategy/order_executor/layering_executor3.h"

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <optional>
#include <random>
#include <utility>

#include <gtest/gtest.h>
#include <pthread.h>
#include <sys/types.h>

#include "coin/proto/coin_market_enums.pb.h"
#include "coin2/base/config/args.h"
#include "coin2/exchange/base/symbology/product_encyclopedia.h"
#include "coin2/strategy/order_executor.pb.h"
#include "coin2/strategy/order_executor/layering_executor.h"
#include "coin2/strategy/order_executor/mm_executor.h"
#include "coin2/strategy/strategy.h"

// extern char **environ;

namespace coin2::strategy::order_executor {

using IProduct = coin2::exchange::base::symbology::IProduct;
using coin2::exchange::base::symbology::ProductEncyclopedia;
using coin2::strategy::order_executor::LayeringExecutor3;
using OrderSpec = coin2::exchange::base::order::OrderSpec;
using OrderContext = coin2::exchange::base::order::OrderContext;
using coin::proto::OrderDuration;
using coin::proto::OrderEvent;
using coin::proto::OrderSide;
using coin::proto::OrderType;
using coin::proto::TradeSide;

const std::string symbol = "XRP-USDT";
const std::string mea_str = "Spot.Huobi.v1";
const std::string tag = "test";
const int64_t ts = 100000;
const double qty_thold = 2000.0;
// const auto product = CreateProductFromUniqueString(symbol, ts);
// ProductEncyclopedia product_cache;
// const auto pi = &(product_cache.holder(*product.get()).product_info());
const auto order_type = OrderType::LIMIT_ORDER;
const auto order_duration = OrderDuration::GTC_ORDER;

class PsudoStrategy : public ::coin2::strategy::IStrategy {
 public:
  PsudoStrategy(
      const std::string& symbol,
      const std::string& mea,
      const int64_t& ts,
      const double& qty_thold)
      : ts_(ts), qty_thold_(qty_thold) {
    config_.set_mea(mea);
    config_.set_symbol(symbol);
    config_.set_order_update_period(100000000);
  }
  void Init(::coin2::app::Driver* driver) {
    driver_ = driver;
    LOG(INFO) << "SYSTEMS: " << driver_->order()->GetAllSystems().size();
    os_ = driver_->order()->GetUniqueSystem(MarketExchangeApi::FromString(config_.mea()));
    auto get_product_holder =
        [this](const coin2::exchange::base::symbology::IProduct& product, int64_t timestamp) {
          return driver_->product_holder_manager()->GetProductHolder(product, timestamp);
        };
    executor_ = std::make_unique<LayeringExecutor3>(
        config_,
        os_,
        ts_,
        nullptr,
        get_product_holder,
        qty_thold_,
        std::nullopt);
    LOG(INFO) << "executor done";
  }

 public:
  int64_t ts_;
  double qty_thold_;
  PassiveExecutorConfig config_;
  ::coin2::app::Driver* driver_{nullptr};
  IOrderSubsystem* os_{nullptr};
  std::unique_ptr<LayeringExecutor3> executor_{nullptr};
};

class LayeringExecutor3Test : public ::testing::Test {
 protected:
  void SetUp() override {
    coin2::app::DriverConfig app;

    using coin2::base::config::GetEnclosingInterval;
    using coin2::base::config::ParseDriverOptions;
    using coin2::base::config::ParseIntervalOptions;
    using coin2::base::config::ParseProductOptions;

    std::experimental::filesystem::path file_path = fmt::format(
        "{}/cc/testcoin2/strategy/order_executor/layering_executor_driver.json",
        std::getenv("COIN_REPO"));
    LOG(INFO) << file_path;
    app.MergeFrom(coin2::base::config::DriverConfigFromJson(file_path));
    // (&app)->mutable_feed()->mutable_common()->mutable_archive()->mutable_interval()->set_
    // ParseIntervalOptions(
    //     res,
    //     (&app)->mutable_feed()->mutable_common()->mutable_archive()->mutable_interval());
    // ParseProductOptions(res, (&app)->mutable_feed());
    auto* mut_sim_config = (&app)->mutable_order()->mutable_common()->mutable_sim_config();
    mut_sim_config->set_result_format(
        coin2::exchange::order::SimConfig::RESULT_FORMAT_PROTO_FILE_DUMP);
    auto begin_end = GetEnclosingInterval(app);
    mut_sim_config->set_start_timestamp(begin_end.first);
    mut_sim_config->set_end_timestamp(begin_end.second);
    strat_ = std::make_unique<PsudoStrategy>(
        symbol,
        mea_str,
        app.order().common().sim_config().start_timestamp(),
        qty_thold);
    // app.mutable_order()->mutable_exchanges()->mutable_sim_config()->set_start_timestamp(
    //     app.order().common().sim_config().start_timestamp());
    for (auto& [name, config] : *(app.mutable_order()->mutable_exchanges())) {
      config.mutable_sim_config()->set_start_timestamp(
          app.order().common().sim_config().start_timestamp());
    }
    coin2::app::impl::Driver driver(app, strat_.get());
    strat_->Init(&driver);
  }
  std::unique_ptr<PsudoStrategy> strat_;
};

std::vector<std::unique_ptr<OrderContext>> GenerateOrders(
    const coin2::exchange::base::symbology::IProduct* product,
    const std::vector<double>& prices,
    const std::vector<double>& qtys,
    int side) {
  std::random_device rd;
  std::mt19937_64 eng(rd());
  std::uniform_int_distribution<int64_t> distr;
  const auto order_side = side == 1 ? OrderSide::BUY_ORDER : OrderSide::SELL_ORDER;
  std::vector<std::unique_ptr<OrderContext>> order_list{};
  CHECK_EQ(prices.size(), qtys.size());
  const auto& num = prices.size();
  for (int i = 0; i < num; ++i) {
    OrderSpec spec(
        *product,
        order_type,
        order_side,
        order_duration,
        prices[i],
        qtys[i],
        ts,
        true,
        0,
        tag);
    auto oc = std::make_unique<OrderContext>(distr(eng), spec);
    order_list.push_back(std::move(oc));
  }
  return order_list;
}

TEST_F(LayeringExecutor3Test, BidCancel) {
  OrderList wo_list;
  const auto prices = std::vector<double>{102.0, 101.0, 100.0};
  const auto qtys = std::vector<double>{90.0, 40.0, 70.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, 1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      1,
      {102, 101, 100},
      {90, 80, 70});  // desired bids -> need to cancel 40 @101

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, 1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(LayeringLevels::Level{LayeringLevels::CANCEL, 101, 40, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, BidEqual) {
  OrderList wo_list;
  const auto prices = std::vector<double>{102.0, 101.0, 100.0};
  const auto qtys = std::vector<double>{90.0, 80.0, 70.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, 1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }

  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      1,
      {102, 101.1, 100},
      {90, 80, 70});  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, 1, wo_list, strat_->executor_.get());
  std::vector<LayeringLevels::Level> expected_res;
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, BidOrder) {
  OrderList wo_list;
  const auto prices = std::vector<double>{100.0, 90.0, 80.0};
  const auto qtys = std::vector<double>{900.0, 800.0, 700.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, 1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices = std::vector<double>{110.0, 90.0, 80.0};
  const auto desired_qtys = std::vector<double>{900.0, 800.0, 700.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, 1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(LayeringLevels::Level{LayeringLevels::CANCEL, 80.0, 700.0, std::nullopt});
  for (const auto& level : expected_res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  LOG(INFO) << "EQUAL: " << (res.back() == expected_res.back());
  ASSERT_EQ(expected_res, res);
  // EXPECT_EQ(1, 1);
  // EXPECT_THAT(res, ::testing::ContainerEq(expected_res));
}

TEST_F(LayeringExecutor3Test, AskOrder1) {
  OrderList wo_list;
  const auto prices =
      std::vector<double>{0.11377, 0.11387, 0.11400, 0.11402, 0.11407, 0.11410, 0.11417, 0.11425};
  const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      -1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at desired_layer 700 @ 0.11387, it's Outside so must order
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(LayeringLevels::Level{LayeringLevels::CANCEL, 0.11425, 200.0, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, AskOrder2) {
  OrderList wo_list;
  const auto prices = std::vector<double>{0.11377,
                                          0.11387,
                                          0.11387,
                                          0.11400,
                                          0.11402,
                                          0.11407,
                                          0.11410,
                                          0.11417,
                                          0.11425};
  const auto qtys =
      std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      -1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at current_order 500.0 @ 0.11407, desired_layer is 400.0 @ 0.11402 and over
  // qty_bp_thold(2,000bp) so CANCEL
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(
      LayeringLevels::Level{LayeringLevels::CANCEL, 0.11407, 500.0, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, AskOrder3) {
  OrderList wo_list;
  const auto prices =
      std::vector<double>{0.11377, 0.11387, 0.11387, 0.11400, 0.11402, 0.11410, 0.11417, 0.11425};
  const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 400.0, 300.0, 200.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      -1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at desired_layer 200 @ 0.11412, it's Outside so must order
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(LayeringLevels::Level{LayeringLevels::CANCEL, 0.11417, 300.0, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, AskOrder4) {
  OrderList wo_list;
  const auto prices = std::vector<double>{0.11377,
                                          0.11387,
                                          0.11387,
                                          0.11400,
                                          0.11402,
                                          0.11410,
                                          0.11425};
  const auto qtys =
      std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 400.0, 200.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      -1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at desired_layer 200 @ 0.11412, it's Outside so must order
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(
      LayeringLevels::Level{LayeringLevels::CANCEL, 0.11425, 200.0, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, AskOrder5) {
  OrderList wo_list;
  const auto prices =
      std::vector<double>{0.11377, 0.11387, 0.11387, 0.11400, 0.11402, 0.11410};
  const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 400.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at desired_layer 200 @ 0.11412, it's Outside so must order
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(
      LayeringLevels::Level{LayeringLevels::CANCEL, 0.11410, 400.0, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, AskOrder6) {
  OrderList wo_list;
  const auto prices =
      std::vector<double>{0.11377, 0.11387, 0.11387, 0.11400, 0.11402};
  const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at desired_layer 200 @ 0.11412, it's Outside so must order
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(
      LayeringLevels::Level{LayeringLevels::POST, 0.11402, 400.0, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, AskOrder7) {
  OrderList wo_list;
  const auto prices = std::vector<double>{0.11377, 0.11387, 0.11387, 0.11400, 0.11402, 0.11402};
  const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 400.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at desired_layer 200 @ 0.11412, it's Outside so must order
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(
      LayeringLevels::Level{LayeringLevels::POST, 0.11407, 300.0, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, AskOrder8) {
  OrderList wo_list;
  const auto prices = std::vector<double>{0.11377, 0.11387, 0.11387, 0.11400, 0.11402, 0.11402, 0.11407};
  const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 400.0, 300.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at desired_layer 200 @ 0.11412, it's Outside so must order
  std::vector<LayeringLevels::Level> expected_res;
  expected_res.push_back(LayeringLevels::Level{LayeringLevels::POST, 0.11412, 200.0, std::nullopt});
  ASSERT_EQ(expected_res, res);
}

TEST_F(LayeringExecutor3Test, AskOrder9) {
  OrderList wo_list;
  const auto prices = std::vector<double>{0.11377, 0.11387, 0.11387, 0.11400, 0.11402, 0.11402, 0.11407, 0.11412};
  const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 400.0, 300.0, 200.0};
  auto order_list =
      GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
  for (const auto& wo : order_list) {
    wo_list.push_back(wo.get());
  }
  const auto desired_prices =
      std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
  const auto desired_qtys =
      std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
  const auto desired_list = LayeringLevels(
      &(strat_->executor_->product_info()),
      1,
      desired_prices,
      desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

  auto res = strat_->executor_
                 ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
  for (const auto& level : res) {
    LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
  }
  // at desired_layer 200 @ 0.11412, it's Outside so must order
  std::vector<LayeringLevels::Level> expected_res;

  ASSERT_EQ(expected_res, res);
}

// TEST_F(LayeringExecutor3Test, AskOrder10) {
//   OrderList wo_list;
//   const auto prices =
//       std::vector<double>{0.11377, 0.11387, 0.11387, 0.11400, 0.11402, 0.11402, 0.11407};
//   const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 400.0, 300.0};
//   auto order_list =
//       GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
//   for (const auto& wo : order_list) {
//     wo_list.push_back(wo.get());
//   }
//   const auto desired_prices =
//       std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
//   const auto desired_qtys =
//       std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
//   const auto desired_list = LayeringLevels(
//       &(strat_->executor_->product_info()),
//       1,
//       desired_prices,
//       desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

//   auto res = strat_->executor_
//                  ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
//   for (const auto& level : res) {
//     LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
//   }
//   // at desired_layer 200 @ 0.11412, it's Outside so must order
//   std::vector<LayeringLevels::Level> expected_res;
//   expected_res.push_back(LayeringLevels::Level{LayeringLevels::POST, 0.11412, 200.0, std::nullopt});
//   ASSERT_EQ(expected_res, res);
// }

// TEST_F(LayeringExecutor3Test, AskOrder11) {
//   OrderList wo_list;
//   const auto prices =
//       std::vector<double>{0.11377, 0.11387, 0.11387, 0.11400, 0.11402, 0.11402, 0.11407, 0.11412};
//   const auto qtys = std::vector<double>{900.0, 800.0, 700.0, 700.0, 600.0, 400.0, 300.0, 200.0};
//   auto order_list =
//       GenerateOrders(&(strat_->executor_->product()), prices, qtys, -1);  // current bids
//   for (const auto& wo : order_list) {
//     wo_list.push_back(wo.get());
//   }
//   const auto desired_prices =
//       std::vector<double>{0.11376, 0.11386, 0.11387, 0.11392, 0.11397, 0.11402, 0.11407, 0.11412};
//   const auto desired_qtys =
//       std::vector<double>{900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0};
//   const auto desired_list = LayeringLevels(
//       &(strat_->executor_->product_info()),
//       1,
//       desired_prices,
//       desired_qtys);  // desired bids -> different qty but inside qty_thold_bp

//   auto res = strat_->executor_
//                  ->GetMostDesiredModification(desired_list, 0, -1, wo_list, strat_->executor_.get());
//   for (const auto& level : res) {
//     LOG(INFO) << level.opertype << " " << level.qty << " @ " << level.price;
//   }
//   // at desired_layer 200 @ 0.11412, it's Outside so must order
//   std::vector<LayeringLevels::Level> expected_res;
//   ASSERT_EQ(expected_res, res);
// }

}  // namespace coin2::strategy::order_executor
